/*
 * Copyright 2008 Karel Klic.
 * This file is part of Oslavanský Bot.
 * 
 * Oslavanský Bot is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package categorybot;

import java.util.*;
import java.util.regex.*;
import org.apache.log4j.*;

/**
 * Tvary slov.
 */
public class DerivativeFormDetector {
  
  static Logger log = Logger.getLogger(DerivativeFormDetector.class);

  /**
   * Derivative form is determined by searching in phrases list of words.
   * At least one of the phrases must be found in the text.
   * Each phrase consists of words. The text must contain all the words.
   * Order of words does not matter.
   */
  private ArrayList<ArrayList<Pattern>> phrases;
  
  // [^\\] means do not search inside [[]].
  private String[] wordForm = new String[]{"podstatného jména", "přídavného jména",
    "zájmena[^\\]]", "číslovky[^\\]]", "slovesa[^\\]]", "příslovce[^\\]]", "předložky[^\\]]",
    "spojky[^\\]]", "částice[^\\]]", "citoslovce[^\\]]"
  };
  private String[] formTime = new String[]{"přítomný čas", "minulý čas", "budoucí čas",
    "přítomného času", "minulého času", "budoucího času", "času přítomného",
    "času minulého", "času budoucího"
  };
  private String[] abstractType = new String[]{"slova", "slovního spojení", "fráze"};
  private String[] substantivumForms = new String[]{"nominativ", "genitiv", "dativ",
    "akuzativ", "vokativ", "lokál", "instrumentál"
  };
  private String[] amount = new String[]{"jednotné číslo", "jednotného čísla",
    "množné číslo", "množného čísla", "singulár", "plurál"
  };
  private String[] participle = new String[]{"přechodník přítomný",
    "přechodník minulý", "přechodník budoucí"
  };
  private static DerivativeFormDetector instance = null;

  public static DerivativeFormDetector getInstance() {
    if (instance == null) {
      instance = new DerivativeFormDetector();
    }
    return instance;
  }

  private DerivativeFormDetector() {
    phrases = new ArrayList<ArrayList<Pattern>>();
    combine("\\btvar\\b", abstractType);
    combine("\\btvar\\b", wordForm);
    combine("\\bod\\b", abstractType);
    combine("\\bod\\b", wordForm);
    combine("\\bod\\b", participle);
    combine("\\bslovesa[^\\]]", participle);
    combine("\\bpodmiňovací způsob\\b", amount);
    combine(participle, abstractType);
    combine(amount, substantivumForms);
    combine(amount, formTime);
    combine(amount, wordForm);
    combine(abstractType, substantivumForms);
    combine(wordForm, formTime);
    add("rozkazovací způsob slovesa");
  }

  public boolean isDerivativeForm(String text) {
   
    boolean containsDenyPhrase = text.contains("Substantivum (cs)") ||
            text.contains("Substantivum (sk)") || 
            text.contains("Substantivum (sv)");
    
    if (containsDenyPhrase)
      return false;
        
    // Remove section === etymologie === from the text.
    Matcher etymologie = PatternFactory.getHeader3Pattern("etymologie").matcher(text);
    if (etymologie.find()) {
      Matcher etymologieNext = PatternFactory.getHeader3Pattern().matcher(text);
      int etymologieEnd = text.length();
      if (etymologieNext.find(etymologie.end()))
        etymologieEnd = etymologieNext.start();
      
      StringBuilder builder = new StringBuilder();
      builder.append(text.substring(0, etymologie.start()));
      builder.append(text.substring(etymologieEnd));
      text = builder.toString();
    }
    
    StringTokenizer tokenizer = new StringTokenizer(text, "\n");

    // Check it line by line.
    // All the derivative form detectors must be on one line to succeed.
    while (tokenizer.hasMoreTokens()) {
      String line = tokenizer.nextToken();
      for (ArrayList<Pattern> phrase : phrases) {
        boolean containsAll = true;
        for (Pattern word : phrase) {
          if (!word.matcher(line).find()) {
            containsAll = false;
            break;
          }
        }

        if (containsAll) {
          // Log the chosen phrase.
          StringBuilder logMessage = new StringBuilder();
          logMessage.append("der(");
          for (Pattern word : phrase) {
            logMessage.append(word.toString() + "-");
          }
          logMessage.append(")");
          log.info(logMessage.toString());
          // Report success.
          return true;
        }
      }
    }

    return false;
  }

  private void combine(String a, String[] wordList) {
    for (String word : wordList) {
      add(a, word);
    }
  }

  private void combine(String[] wordList0, String[] wordList1) {
    for (String word0 : wordList0) {
      for (String word1 : wordList1) {
        add(word0, word1);
      }
    }
  }

  private void add(String... words) {
    ArrayList<Pattern> phrase = new ArrayList<Pattern>();
    for (String word : words) {
      phrase.add(Pattern.compile(word));
    }
    phrases.add(phrase);
  }
}
